﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Web.Caching;
using System.Configuration;
using System.Collections.Specialized;

namespace MakeItWeb.Framework
{
    public class MakeItWebSiteMapProvider : StaticSiteMapProvider
    {
        private readonly object _lock = new object();
        private SiteMapNode _root;

        public override SiteMapNode BuildSiteMap()
        {
            lock (_lock)
            {
                // Return immediately if this method has been called before
                if (_root != null) return _root;

                // Query the database for site map nodes
                using (DataSet ds = GenericQry.GetDataSet("proc_cm_ListSiteMapNode", new StoredProcArgs()))
                {
                    CreateSiteMapNode(ds.Tables[0]);
                }

                System.Web.HttpContext.Current.Cache.Add("SiteMapCache", true, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.High, null);

                // Return the root SiteMapNode
                return _root;
            }
        }

        private void CreateSiteMapNode(DataTable dt)
        {
            DataRow[] aDr = dt.Select("parent_node_id is null");
            if (aDr.Length > 1)
                throw new Exception("Cannot have more than one root.");

            _root = CreateSiteMapNodeFromDataRow(aDr[0]);

            AddChildNode(_root, dt);
        }

        private SiteMapNode CreateSiteMapNodeFromDataRow(DataRow dr)
        {
            string sID = dr.IsNull("node_cd") ? dr["node_id"].ToString() : dr["node_cd"].ToString();
            string sTitle = dr["node_title"].ToString();
            bool bUseGenericPage = (bool)dr["use_generic_page_ind"];
            string sPageTypeUrl = dr.IsNull("generic_page_type") ? string.Empty : GetUrlForGenericPage((string)dr["generic_page_type"]);
            string sUrl = bUseGenericPage ? string.Format(sPageTypeUrl, sID) : dr.IsNull("node_url") ? string.Empty : dr["node_url"].ToString();
            string sDesc = dr.IsNull("node_description") ? string.Empty : dr["node_description"].ToString();
            string sRole = dr.IsNull("role") ? "*" : dr["role"].ToString();
            return new SiteMapNode(this, sID, sUrl, sTitle, sDesc, string.IsNullOrEmpty(sRole) ? null : new string[] { sRole }, null, null, null);
        }

        public static string GetUrl(string sNodeCd, string sPageType)
        {
            string sGenericPageType = GetUrlForGenericPage(sPageType);
            if (!string.IsNullOrEmpty(sNodeCd) && !string.IsNullOrEmpty(sGenericPageType))
                return string.Format(sGenericPageType, sNodeCd);

            return null;
        }

        private static string GetUrlForGenericPage(string sPageType)
        {
            var types = ConfigurationManager.GetSection("DynamicPageTypes") as NameValueCollection;
            if (types != null)
                return types[sPageType];

            throw new ArgumentException("Invalid page type.");
        }

        private void AddChildNode(SiteMapNode node, DataTable dt)
        {
            DataRow[] aDr = dt.Select(string.Format("parent_node_id = '{0}'", node.Key));
            foreach (DataRow dr in aDr)
            {
                SiteMapNode cNode = CreateSiteMapNodeFromDataRow(dr);
                base.AddNode(cNode, node);
                AddChildNode(cNode, dt);
            }
        }

        public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
        {
            if (node.Roles != null)
                return node.Roles[0].ToString() == "*" || HttpContext.Current.User.IsInRole(node.Roles[0].ToString());

            return base.IsAccessibleToUser(context, node);
        }

        protected override SiteMapNode GetRootNodeCore()
        {
            if (System.Web.HttpContext.Current.Cache.Get("SiteMapCache") == null)
            {
                _root = null;
                base.Clear();
            }
            BuildSiteMap();
            return _root;
        }
    }
}